package com.mybatis.lo.dao;

import com.mybatis.lo.model.Configuration;
import com.mybatis.lo.model.DynamicSqlSource;
import com.mybatis.lo.model.IfSqlNode;
import com.mybatis.lo.model.MappedStatement;
import com.mybatis.lo.model.MixedSqlNode;
import com.mybatis.lo.model.RawSqlSource;
import com.mybatis.lo.model.SqlNode;
import com.mybatis.lo.model.SqlSource;
import com.mybatis.lo.model.StaticTextSqlNode;
import com.mybatis.lo.model.TextSqlNode;
import com.mybatis.lo.model.WhereSqlNode;
import com.mysql.jdbc.StringUtils;
import javafx.util.Pair;
import lombok.Getter;
import org.apache.commons.dbcp.BasicDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * v3版本加载配置相关过程
 * @author zhangbh
 * 2023-06-11 20:32
 **/
public class UserDaoImplWithJdbc3LoadConfig {

    @Getter
    private Configuration configuration;

    public Configuration loadConfig(String configPath) {
        configuration = new Configuration();
        InputStream inputStream = getInputStream(configPath);

        Document document = createDocument(inputStream);
        // 加载环境
        loadEnvironment(document.getRootElement());
        // 加载mappers映射配置文件
        loadMappers(document.getRootElement());
        return configuration;
    }

    private Document createDocument(InputStream inputStream) {
        SAXReader reader = new SAXReader();
        try {
            return reader.read(inputStream);
        } catch (DocumentException e) {
            System.out.println("解析配置文件异常");
            throw new RuntimeException(e);
        }
    }

    private InputStream getInputStream(String configurationPath) {
        return this.getClass().getClassLoader().getResourceAsStream(configurationPath);
    }

    private void loadMappers(Element rootElement) {
        Element mappers = rootElement.element("mappers");
        List<Element> mapperList = mappers.elements("mapper");
        for (Element mapperElement : mapperList) {
            loadMapper(mapperElement);
        }
    }

    private void loadMapper(Element mapper) {
        String resource = mapper.attributeValue("resource");
        InputStream inputStream = getInputStream(resource);
        Document document = createDocument(inputStream);
        Element mapperElement = document.getRootElement();
        String namespace = mapperElement.attributeValue("namespace");
        if (StringUtils.isNullOrEmpty(namespace)) {
            System.out.println("没有命名空间");
            return;
        }
        loadMappedStatements(namespace, mapperElement);
    }

    private void loadMappedStatements(String namespace, Element mapperElement) {
        // todo 只处理 select 标签
        List<Element> selectElements = mapperElement.elements("select");
        for (Element selectElement : selectElements) {
            String id = selectElement.attributeValue("id");
            String resultType = selectElement.attributeValue("resultType");
            String statementType = selectElement.attributeValue("statementType");
            // 加载SqlSource
            SqlSource sqlSource = loadSqlSource(selectElement);
            MappedStatement mappedStatement = new MappedStatement(id, statementType, sqlSource, resultType);
            configuration.addMappedStatement(namespace + "." + id, mappedStatement);
        }
    }

    private SqlSource loadSqlSource(Element selectElement) {

        // key=isDynamic，value=复合节点对象
        Pair<Boolean, SqlNode> pair = loadSqlNodes(selectElement);

        SqlNode mixedSqlNode = pair.getValue();
        if (pair.getKey()) {
            return new DynamicSqlSource(mixedSqlNode);
        } else {
            return new RawSqlSource(mixedSqlNode);
        }
    }

    private Pair<Boolean, SqlNode> loadSqlNodes(Element selectElement) {
        boolean isDynamic = false;
        SqlNode mixedSqlNode = null;
        List<SqlNode> nodes = new ArrayList<>();
        int nodeCount = selectElement.nodeCount();
        // 遍历所有内容
        for (int i = 0; i < nodeCount; i++) {
            Node node = selectElement.node(i);
            // 判断节点是Text 或者是Element元素
            if (node instanceof Text) {
                String text = node.getText().trim();
                if (StringUtils.isNullOrEmpty(text)) {
                    continue;
                }
                // 判断是否存在${符号
                boolean contains = text.contains("${");
                if (contains) {
                    isDynamic = true;
                    nodes.add(new TextSqlNode(text));
                } else {
                    nodes.add(new StaticTextSqlNode(text));
                }
            } else if (node instanceof Element) {
                isDynamic = true;
                // 判断一下if、where标签
                if (node.getName().equals("if")) {
                    String test = ((Element) node).attributeValue("test");
                    nodes.add(new IfSqlNode(test, loadSqlNodes((Element) node).getValue()));
                } else if (node.getName().equals("where")) {
                    nodes.add(new WhereSqlNode(loadSqlNodes((Element) node).getValue()));
                } else {
                    // todo 其他标签也没有处理
                    System.out.println("解析到未处理标签");
                }
            } else {
                // todo 其他类型的节点信息，没去处理
            }
        }
        mixedSqlNode = new MixedSqlNode(nodes);

        return new Pair<>(isDynamic, mixedSqlNode);
    }

    private void loadEnvironment(Element rootElement) {
        Element environments = rootElement.element("environments");
        String configDefault = environments.attributeValue("default");
        if (StringUtils.isNullOrEmpty(configDefault)) {
            System.out.println("指定数据库为空，直接退出");
            return;
        }
        loadDefaultEnvironment(environments, configDefault);
    }

    /**
     * 加载默认环境
     * @param environments 所有环境
     * @param configDefault 默认环境
     */
    private void loadDefaultEnvironment(Element environments, String configDefault) {
        List<Element> environment = environments.elements("environment");
        for (Element element : environment) {
            String id = element.attributeValue("id");
            if (StringUtils.isNullOrEmpty(id) || !configDefault.equals(id)) {
                continue;
            }
            loadDataSource(element.element("datasource"));
        }
    }

    /**
     * 处理 <datasource>
     * @param datasource 数据源标签
     */
    private void loadDataSource(Element datasource) {
        String dbType = datasource.attributeValue("type");
        if ("DBCP".equals(dbType)) {
            BasicDataSource dataSource = new BasicDataSource();
            Properties properties = loadProperties(datasource);
            dataSource.setDriverClassName(properties.getProperty("driverClassName"));
            dataSource.setUrl(properties.getProperty("url"));
            dataSource.setUsername(properties.getProperty("username"));
            dataSource.setPassword(properties.getProperty("password"));
            configuration.setDataSource(dataSource);
        } else {
            // todo 可补充其他数据源支持
            System.out.println("暂不支持的数据源");
        }

    }

    /**
     * 解析datasource.property标签
     * @param datasource 数据库配置标签
     * @return 配置属性
     */
    private Properties loadProperties(Element datasource) {
        Properties properties = new Properties();
        List<Element> propertys = datasource.elements("property");
        for (Element property : propertys) {
            String name = property.attributeValue("name");
            String value = property.attributeValue("value");
            properties.setProperty(name, value);
        }
        return properties;
    }
}
